package com.tianji.learning.service.impl;

import com.tianji.common.autoconfigure.mq.RabbitMqHelper;
import com.tianji.common.constants.MqConstants;
import com.tianji.common.utils.CollUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.constants.RedisConstants;
import com.tianji.learning.domain.vo.SignResultVO;
import com.tianji.learning.mq.SignInMessage;
import com.tianji.learning.service.ISignRecordService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.connection.BitFieldSubCommands;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;

@Slf4j
@Service
@RequiredArgsConstructor
public class SignRecordServiceImpl implements ISignRecordService {
	private final StringRedisTemplate redisTemplate;
	private final RabbitMqHelper mqHelper;
	
	@Override
	public SignResultVO addSignRecords() {
		//1.获取当前登录用户
		Long userId = UserContext.getUser();
		//2.拼接Redis的Key
		LocalDateTime now = LocalDateTime.now();
		String format = now.format(DateTimeFormatter.ofPattern("yyyyHH"));//得到年月
		String key = RedisConstants.SIGN_RECORD_KEY_PREFIX + userId.toString() + format;
		//3.利用bitset命令,将签到记录保存到Redis的BitMap结构中  需要判断返回值 如果为True则表示已经签到过
		int offset = now.getDayOfMonth() - 1;//偏移量
		Boolean setBit = redisTemplate.opsForValue().setBit(key, offset, true);
		if (Boolean.TRUE.equals(setBit)) {
			throw new RuntimeException("已经签到过");
		}
		//4.计算连续签到的天数
		int days = countSignDays(key, now.getDayOfMonth());
		//5.计算连续签到的奖励积分
		// 3.计算签到得分
		int rewardPoints = 0;
		switch (days) {
			case 7:
				rewardPoints = 10;
				break;
			case 14:
				rewardPoints = 20;
				break;
			case 28:
				rewardPoints = 40;
				break;
		}
		//6.  保存积分
		mqHelper.send(
				MqConstants.Exchange.LEARNING_EXCHANGE,
				MqConstants.Key.SIGN_IN,
				SignInMessage.of(userId, rewardPoints + 1));
		//7.封装VO返回
		SignResultVO vo = new SignResultVO();
		vo.setSignDays(days);
		vo.setRewardPoints(rewardPoints);
		return vo;
	}
	
	@Override
	public Byte[] querySignRecords() {
		//1.获取当前用户id
		Long userId = UserContext.getUser();
		//2.凭借Redis的key
		LocalDateTime now = LocalDateTime.now();
		String format = now.format(DateTimeFormatter.ofPattern("yyyyHH"));//得到年月
		String key = RedisConstants.SIGN_RECORD_KEY_PREFIX + userId.toString() + format;
		//3.使用Redis的bitmap命令查询本月第一天到今天的签到记录
		int offset = now.getDayOfMonth() - 1;
		int dayOfMonth = now.getDayOfMonth();
		List<Long> bitField = redisTemplate.opsForValue()
				.bitField(key, BitFieldSubCommands.create()
						.get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth)).valueAt(0));
		if (CollUtils.isEmpty(bitField)) {
			return new Byte[0];
		}
		Long num = bitField.get(0);
		//4.利用"与运算" 和位移封装结果
		Byte[] bytes = new Byte[dayOfMonth];
		while (offset>=0){
			bytes[offset] = (byte) (num & 1);
			offset--;
			num = num >>> 1;
		}
		return bytes;
	}
	
	/**
	 * @param key        存到缓存中的KEY
	 * @param dayOfMonth 本月第一天到今天的天数
	 * @return 连续签到天数
	 */
	private int countSignDays(String key, int dayOfMonth) {
		//1.求本月第一天到今天的所有签到数据  bitfiled :key get  u天数  0  查询KEY无符号从0索引开始到本月第一天到今天的天数 得到的是十进制
		List<Long> longs = redisTemplate
				.opsForValue()
				.bitField(key, BitFieldSubCommands.create()
						.get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth))
						.valueAt(0));
		if (CollUtils.isEmpty(longs)) {
			return 0;
		}
		//2.求出本月签到的天数  使用与运算,最后一位于1比较,如果为1则表示签到.然后右移一位
		Long num = longs.get(0);
		log.info("num:{}", num);
		int count = 0;
		while ((num & 1) == 1) {
			count++;
			num = num >>> 1;//右移一位
			
		}
		return count;
	}
}
